markdown-text-editor 2.0.0-beta.0 → 2.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +78 -4
- package/dist/markdown-text-editor.es.js +23 -16
- package/dist/markdown-text-editor.es.js.map +1 -1
- package/dist/markdown-text-editor.min.js +6 -6
- package/dist/markdown-text-editor.min.js.map +1 -1
- package/dist/markdown-text-editor.umd.js +6 -6
- package/dist/markdown-text-editor.umd.js.map +1 -1
- package/locale/ar.js +55 -0
- package/locale/az.js +55 -0
- package/locale/bg.js +55 -0
- package/locale/bn.js +55 -0
- package/locale/bs.js +55 -0
- package/locale/ca.js +55 -0
- package/locale/cs.js +55 -0
- package/locale/da.js +55 -0
- package/locale/de.js +55 -0
- package/locale/el.js +55 -0
- package/locale/es.js +55 -0
- package/locale/et.js +55 -0
- package/locale/fa.js +55 -0
- package/locale/fi.js +55 -0
- package/locale/fil.js +55 -0
- package/locale/fr.js +55 -0
- package/locale/ga.js +55 -0
- package/locale/he.js +55 -0
- package/locale/hi.js +55 -0
- package/locale/hr.js +55 -0
- package/locale/hu.js +55 -0
- package/locale/hy.js +55 -0
- package/locale/id.js +55 -0
- package/locale/is.js +55 -0
- package/locale/it.js +55 -0
- package/locale/ja.js +55 -0
- package/locale/kn.js +55 -0
- package/locale/ko.js +55 -0
- package/locale/locale.d.ts +10 -0
- package/locale/lt.js +55 -0
- package/locale/lv.js +55 -0
- package/locale/mk.js +55 -0
- package/locale/mr.js +55 -0
- package/locale/ms.js +55 -0
- package/locale/mt.js +55 -0
- package/locale/nl.js +55 -0
- package/locale/no.js +55 -0
- package/locale/pl.js +55 -0
- package/locale/pt.js +55 -0
- package/locale/ro.js +55 -0
- package/locale/ru.js +55 -0
- package/locale/sk.js +55 -0
- package/locale/sl.js +55 -0
- package/locale/sq.js +55 -0
- package/locale/sr.js +55 -0
- package/locale/sv.js +55 -0
- package/locale/sw.js +55 -0
- package/locale/ta.js +55 -0
- package/locale/te.js +55 -0
- package/locale/template.js +62 -0
- package/locale/th.js +55 -0
- package/locale/tr.js +55 -0
- package/locale/uk.js +55 -0
- package/locale/ur.js +55 -0
- package/locale/uz.js +55 -0
- package/locale/vi.js +55 -0
- package/locale/zh-hk.js +55 -0
- package/locale/zh.js +55 -0
- package/package.json +13 -2
- package/types/index.d.ts +16 -5
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ A lightweight, embeddable JavaScript Markdown editor that transforms a standard
|
|
|
13
13
|
|
|
14
14
|
A native-first Markdown editor built on a standard textarea. No data binding, no API — just drop it in and your forms keep working as-is.
|
|
15
15
|
|
|
16
|
-
**Works standalone.** No Frutjam, no Tailwind, no framework required
|
|
16
|
+
**Works standalone.** No Frutjam, no Tailwind, no framework required. One script and one stylesheet.
|
|
17
17
|
|
|
18
18
|

|
|
19
19
|
|
|
@@ -40,7 +40,7 @@ Most JavaScript markdown editors (EasyMDE, SimpleMDE, CodeMirror-based editors)
|
|
|
40
40
|
| Swap the markdown parser | ✅ | ❌ |
|
|
41
41
|
| Keyboard shortcuts | ✅ | Partial |
|
|
42
42
|
| Dark mode / theming | ✅ | Limited |
|
|
43
|
-
| Bundle size, gzipped |
|
|
43
|
+
| Bundle size, gzipped | 56 KB (JS + CSS) | 107 KB (JS + CSS) |
|
|
44
44
|
|
|
45
45
|
## 🚀 Quick Start
|
|
46
46
|
|
|
@@ -90,6 +90,22 @@ Import the stylesheet **or** link it, never both. If you forget it entirely the
|
|
|
90
90
|
|
|
91
91
|
That's it. Form submission, `.value` access, and all native textarea behaviour work exactly as before.
|
|
92
92
|
|
|
93
|
+
### Upgrading from 1.x
|
|
94
|
+
|
|
95
|
+
**Add the stylesheet.** It used to be carried inside the JavaScript and injected as a `<style>` element. It is now a real file, so an existing setup keeps working but renders unstyled until you link or import it. The editor says so in the console rather than leaving you to guess.
|
|
96
|
+
|
|
97
|
+
```javascript
|
|
98
|
+
import 'markdown-text-editor/style.css'; // bundlers
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
```html
|
|
102
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/markdown-text-editor/dist/markdown-text-editor.css">
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
If you load the editor from an unpinned CDN URL, this reaches you the moment 2.0.0 publishes. Pin a version if you are not ready.
|
|
106
|
+
|
|
107
|
+
That is the whole breaking change. `labels` was renamed to `locale` and still works under the old name, so nothing else needs touching.
|
|
108
|
+
|
|
93
109
|
## ✨ Features
|
|
94
110
|
|
|
95
111
|
- 🔌 **Native Form Integration** — Works exactly like a standard `<textarea>`. No complex APIs — just use `.value` or the `name` attribute. Compatible with Django, Laravel, PHP, Rails, Node.js, and with React or Vue via a ref and `destroy()` on unmount
|
|
@@ -106,12 +122,14 @@ That's it. Form submission, `.value` access, and all native textarea behaviour w
|
|
|
106
122
|
- 🛡️ **XSS Safe** — Preview output sanitized via [DOMPurify](https://github.com/cure53/DOMPurify) before rendering, including the output of a custom renderer
|
|
107
123
|
- 🛡️ **CSP Compatible** — No inline event handlers, no `eval`, and no injected `<style>` element. The stylesheet is a real file you link, so a strict policy needs no `'unsafe-inline'` exception for either `script-src` or `style-src` ([#42](https://github.com/nezanuha/markdown-text-editor/issues/42))
|
|
108
124
|
- 🌍 **RTL Support** — Native Right-to-Left support for Arabic, Urdu, Farsi, and other RTL languages
|
|
125
|
+
- 🗣️ **Translatable Interface** — Every tooltip, menu item, dialog field and screen reader name goes through the `locale` option, keyed by the English string. `locale/template.js` lists all 45 of them so you don't have to go looking. Fifty-six languages ship with the package; you import the one you need
|
|
109
126
|
- 🌙 **Dark Mode & Theming** — Inherits `data-theme` from any ancestor element. Built-in light, dark, snowberry, and darkberry themes. Fully customizable via CSS variables
|
|
110
127
|
- 🧩 **Custom Tools** — Add your own toolbar button with `{ custom: { title, icon, action } }`, or extend `MarkdownEditor.Tool` when it builds its own markup. Keyboard shortcuts, translation and teardown work exactly as they do for the built-ins. No fork, no patching
|
|
111
128
|
- 🎛️ **Modular Toolbar** — Pick exactly which tools appear and in what order
|
|
112
129
|
- 🟦 **TypeScript Ready** — Definitions ship with the package. Options, toolbar entries and variable shapes are all checked, so a mistyped tool name is a compile error rather than a silently missing button
|
|
130
|
+
- 🖥️ **SSR Safe** — Importing it on the server doesn't throw, so Next.js, Remix and Nuxt need no `dynamic(..., { ssr: false })` wrapper. The `<textarea>` is in the server HTML with its content, and the editor enhances it after hydration, so there's no markup mismatch and nothing pops in
|
|
113
131
|
- 📦 **Universal Module Support** — ESM, CommonJS, UMD, and IIFE. Works with Vite, webpack, Rollup, or directly via `<script src>` CDN — no configuration needed
|
|
114
|
-
- 🚀 **High Performance** — ~
|
|
132
|
+
- 🚀 **High Performance** — ~43KB gzipped JS plus ~14KB gzipped CSS, cached separately. Debounced preview, cached layout calculations, conflict-free Tab/Enter handling for large documents
|
|
115
133
|
|
|
116
134
|
## 🛠 Developer Workflow
|
|
117
135
|
|
|
@@ -189,6 +207,53 @@ new MarkdownEditor('#markdown-editor', { toolbar: ['bold', EmbedTool, 'preview']
|
|
|
189
207
|
|
|
190
208
|
Two helpers exist for tools that need them: `editor.renderMarkdown(md)` returns sanitized HTML exactly as the preview does, and `editor.createToolbar(textarea, ['bold', 'italic'])` builds a real toolbar bound to another textarea, for a tool collecting rich text in its own dialog.
|
|
191
209
|
|
|
210
|
+
### Translate the interface
|
|
211
|
+
|
|
212
|
+
Every tooltip, menu item, dialog field and screen reader name goes through the `locale` option, keyed by the English string. Anything you leave out stays in English.
|
|
213
|
+
|
|
214
|
+
```javascript
|
|
215
|
+
import fr from 'markdown-text-editor/locale/fr.js';
|
|
216
|
+
|
|
217
|
+
new MarkdownEditor('#markdown-editor', { locale: fr });
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
`locale` takes the strings, not a language code. `locale: 'fr'` is a compile error in TypeScript and logs what to write instead in JavaScript. Importing the one language you want is why nobody downloads the other fifty-five.
|
|
221
|
+
|
|
222
|
+
<details>
|
|
223
|
+
<summary><strong>56 languages ship with the package</strong></summary>
|
|
224
|
+
|
|
225
|
+
`ar` Arabic · `az` Azerbaijani · `bg` Bulgarian · `bn` Bengali · `bs` Bosnian · `ca` Catalan · `cs` Czech · `da` Danish · `de` German · `el` Greek · `es` Spanish · `et` Estonian · `fa` Persian · `fi` Finnish · `fil` Filipino · `fr` French · `ga` Irish · `he` Hebrew · `hi` Hindi · `hr` Croatian · `hu` Hungarian · `hy` Armenian · `id` Indonesian · `is` Icelandic · `it` Italian · `ja` Japanese · `kn` Kannada · `ko` Korean · `lt` Lithuanian · `lv` Latvian · `mk` Macedonian · `mr` Marathi · `ms` Malay · `mt` Maltese · `nl` Dutch · `no` Norwegian · `pl` Polish · `pt` Portuguese · `ro` Romanian · `ru` Russian · `sk` Slovak · `sl` Slovenian · `sq` Albanian · `sr` Serbian · `sv` Swedish · `sw` Swahili · `ta` Tamil · `te` Telugu · `th` Thai · `tr` Turkish · `uk` Ukrainian · `ur` Urdu · `uz` Uzbek · `vi` Vietnamese · `zh` Chinese (Simplified) · `zh-hk` Chinese (Hong Kong)
|
|
226
|
+
|
|
227
|
+
English needs no file; it is what the editor falls back to.
|
|
228
|
+
|
|
229
|
+
None of these has been checked by a native speaker yet, so each file exports `reviewed = false` and says so in its header. If one reads wrong to you, [a pull request](https://github.com/nezanuha/markdown-text-editor/issues) correcting a single line is welcome and genuinely useful. Languages with more than one plural form, such as Polish and Arabic, currently get one wording that reads correctly for most counts but not all.
|
|
230
|
+
|
|
231
|
+
</details>
|
|
232
|
+
|
|
233
|
+
Partial is fine, and you can override a shipped translation without forking it:
|
|
234
|
+
|
|
235
|
+
```javascript
|
|
236
|
+
import fr from 'markdown-text-editor/locale/fr.js';
|
|
237
|
+
|
|
238
|
+
new MarkdownEditor('#markdown-editor', {
|
|
239
|
+
locale: { ...fr, 'Image': 'Photo' } // your wording wins
|
|
240
|
+
});
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
**`locale/template.js` lists every string**, English on both sides. Copy it to add a language, or map it through a translation system you already have, which keeps the editor's wording consistent with the rest of your interface:
|
|
244
|
+
|
|
245
|
+
```javascript
|
|
246
|
+
import template from 'markdown-text-editor/locale/template.js';
|
|
247
|
+
|
|
248
|
+
const locale = Object.fromEntries(
|
|
249
|
+
Object.keys(template).map(key => [key, i18n.t(`editor.${key}`)])
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
new MarkdownEditor('#markdown-editor', { locale });
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
Counts carry a `{n}` placeholder, so `'{n} words'` can become `'{n} mots'` or `'mots : {n}'` and the number lands where that language wants it. CSS class names never change, so `.bold-btn` stays `.bold-btn` in every language.
|
|
256
|
+
|
|
192
257
|
### Tear down in SPAs
|
|
193
258
|
|
|
194
259
|
```javascript
|
|
@@ -199,8 +264,15 @@ editor.destroy();
|
|
|
199
264
|
In React, create the editor in an effect and return `destroy` as the cleanup — that also covers StrictMode running effects twice in development:
|
|
200
265
|
|
|
201
266
|
```jsx
|
|
267
|
+
// Options are read once, so reach the latest onChange through a ref rather
|
|
268
|
+
// than rebuilding the editor whenever the parent re-renders
|
|
269
|
+
const onChangeRef = useRef(onChange);
|
|
270
|
+
onChangeRef.current = onChange;
|
|
271
|
+
|
|
202
272
|
useEffect(() => {
|
|
203
|
-
const editor = new MarkdownEditor(ref.current, {
|
|
273
|
+
const editor = new MarkdownEditor(ref.current, {
|
|
274
|
+
onChange: (value) => onChangeRef.current?.(value),
|
|
275
|
+
});
|
|
204
276
|
return () => editor.destroy();
|
|
205
277
|
}, []);
|
|
206
278
|
```
|
|
@@ -209,6 +281,8 @@ Use `defaultValue`, not `value`. The editor writes to the textarea directly, so
|
|
|
209
281
|
|
|
210
282
|
The same two rules cover Svelte, Angular and anything else: don't bind the value, and call `destroy()` on unmount. In Angular, `onChange` fires outside the zone, so wrap it in `zone.run()` for change detection to notice.
|
|
211
283
|
|
|
284
|
+
This is also all you need for server-side rendering. `useEffect`, `onMounted` and `onMount` never run on the server, so the DOM work stays in the browser and importing the package server-side is safe. Next.js, Remix and Nuxt need no `ssr: false` wrapper, and the `<textarea>` ships in the server HTML with its content already in it.
|
|
285
|
+
|
|
212
286
|
Options are read once when the editor is constructed. Changing them later has no effect — destroy the editor and create a new one instead.
|
|
213
287
|
|
|
214
288
|
## 📖 Documentation
|
|
@@ -2866,20 +2866,23 @@ var Yn = "\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"20\" height=\"
|
|
|
2866
2866
|
return this.previewContent;
|
|
2867
2867
|
}
|
|
2868
2868
|
}, er = class {
|
|
2869
|
-
constructor(e, t = {}) {
|
|
2870
|
-
this.options = t, this.footer = document.createElement("div"), this.footer.className = "editor-footer not-prose fj:me-surface fj:me-surface-outline fj:border-base-soft fj:border-0 fj:border-t fj:flex fj:justify-between fj:items-center fj:px-3 fj:py-1 fj:text-xs fj:opacity-60 fj:select-none", this.footer.setAttribute("aria-hidden", "true"), this.positionEl = document.createElement("span"), this.charsEl = document.createElement("span"), this.wordsEl = document.createElement("span"), (this.options.line || this.options.col) && this.footer.appendChild(this.positionEl), this.options.chars && this.footer.appendChild(this.charsEl), this.options.words && this.footer.appendChild(this.wordsEl), e.appendChild(this.footer);
|
|
2869
|
+
constructor(e, t = {}, n = (e) => e) {
|
|
2870
|
+
this.options = t, this.label = n, this.footer = document.createElement("div"), this.footer.className = "editor-footer not-prose fj:me-surface fj:me-surface-outline fj:border-base-soft fj:border-0 fj:border-t fj:flex fj:justify-between fj:items-center fj:px-3 fj:py-1 fj:text-xs fj:opacity-60 fj:select-none", this.footer.setAttribute("aria-hidden", "true"), this.positionEl = document.createElement("span"), this.charsEl = document.createElement("span"), this.wordsEl = document.createElement("span"), (this.options.line || this.options.col) && this.footer.appendChild(this.positionEl), this.options.chars && this.footer.appendChild(this.charsEl), this.options.words && this.footer.appendChild(this.wordsEl), e.appendChild(this.footer);
|
|
2871
2871
|
}
|
|
2872
2872
|
update(e) {
|
|
2873
2873
|
let t = e.value, n = e.selectionStart ?? 0;
|
|
2874
2874
|
if (this.options.line || this.options.col) {
|
|
2875
2875
|
let e = t.substring(0, n).split("\n"), r = [];
|
|
2876
|
-
this.options.line && r.push(
|
|
2876
|
+
this.options.line && r.push(this._count("Ln {n}", e.length)), this.options.col && r.push(this._count("Col {n}", e[e.length - 1].length + 1)), this.positionEl.textContent = r.join(", ");
|
|
2877
2877
|
}
|
|
2878
|
-
if (this.options.chars && (this.charsEl.textContent =
|
|
2878
|
+
if (this.options.chars && (this.charsEl.textContent = this._count("{n} chars", t.length)), this.options.words) {
|
|
2879
2879
|
let e = t.trim() ? t.trim().split(/\s+/).length : 0;
|
|
2880
|
-
this.wordsEl.textContent =
|
|
2880
|
+
this.wordsEl.textContent = this._count("{n} words", e);
|
|
2881
2881
|
}
|
|
2882
2882
|
}
|
|
2883
|
+
_count(e, t) {
|
|
2884
|
+
return this.label(e).replace("{n}", t.toLocaleString());
|
|
2885
|
+
}
|
|
2883
2886
|
}, tr = class {
|
|
2884
2887
|
diff(e, t, n = {}) {
|
|
2885
2888
|
let r;
|
|
@@ -3574,33 +3577,33 @@ var Dr = class {
|
|
|
3574
3577
|
}
|
|
3575
3578
|
_buildPanel() {
|
|
3576
3579
|
let e = document.createElement("div");
|
|
3577
|
-
e.className = "find-replace-panel fj:absolute fj:top-1 fj:right-1 fj:z-20 fj:me-surface fj:me-surface-outline fj:me-surface-rounded fj:p-1.5 fj:flex fj:flex-col fj:gap-1 fj:shadow-lg", e.setAttribute("role", "search"), e.setAttribute("aria-label", "Find and replace");
|
|
3580
|
+
e.className = "find-replace-panel fj:absolute fj:top-1 fj:right-1 fj:z-20 fj:me-surface fj:me-surface-outline fj:me-surface-rounded fj:p-1.5 fj:flex fj:flex-col fj:gap-1 fj:shadow-lg", e.setAttribute("role", "search"), e.setAttribute("aria-label", this.editor.label("Find and replace"));
|
|
3578
3581
|
let t = document.createElement("div");
|
|
3579
|
-
t.className = "fj:flex fj:items-center fj:gap-1", this._findInput = document.createElement("input"), this._findInput.type = "text", this._findInput.placeholder = "Find", this._findInput.className = "fj:me-input fj:me-input-xs fj:w-36", this._findInput.setAttribute("aria-label", "Find"), this._findInput.addEventListener("input", () => this._search()), this._findInput.addEventListener("keydown", (e) => {
|
|
3582
|
+
t.className = "fj:flex fj:items-center fj:gap-1", this._findInput = document.createElement("input"), this._findInput.type = "text", this._findInput.placeholder = this.editor.label("Find"), this._findInput.className = "fj:me-input fj:me-input-xs fj:w-36", this._findInput.setAttribute("aria-label", this.editor.label("Find")), this._findInput.addEventListener("input", () => this._search()), this._findInput.addEventListener("keydown", (e) => {
|
|
3580
3583
|
e.key === "Enter" && (e.shiftKey ? this._prev() : this._next()), e.key === "Escape" && (e.preventDefault(), this.close());
|
|
3581
3584
|
}), this._countEl = document.createElement("span"), this._countEl.className = "fj:text-xs fj:opacity-50 fj:whitespace-nowrap fj:min-w-[3.5rem] fj:text-center fj:select-none", this._countEl.setAttribute("aria-live", "polite");
|
|
3582
3585
|
let n = this._iconBtn("Previous match", "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m18 15-6-6-6 6\"/></svg>");
|
|
3583
3586
|
n.addEventListener("click", () => this._prev());
|
|
3584
3587
|
let r = this._iconBtn("Next match", "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m6 9 6 6 6-6\"/></svg>");
|
|
3585
|
-
r.addEventListener("click", () => this._next()), this._caseSensitiveBtn = document.createElement("button"), this._caseSensitiveBtn.type = "button", this._caseSensitiveBtn.className = "fj:me-btn fj:me-btn-xs fj:me-btn-square fj:me-btn-ghost fj:text-xs fj:font-bold", this._caseSensitiveBtn.textContent = "Aa", this._caseSensitiveBtn.setAttribute("aria-label", "Match case"), this._caseSensitiveBtn.setAttribute("aria-pressed", "false"), this._caseSensitiveBtn.title = "Match case", this._caseSensitiveBtn.addEventListener("click", () => {
|
|
3588
|
+
r.addEventListener("click", () => this._next()), this._caseSensitiveBtn = document.createElement("button"), this._caseSensitiveBtn.type = "button", this._caseSensitiveBtn.className = "fj:me-btn fj:me-btn-xs fj:me-btn-square fj:me-btn-ghost fj:text-xs fj:font-bold", this._caseSensitiveBtn.textContent = "Aa", this._caseSensitiveBtn.setAttribute("aria-label", this.editor.label("Match case")), this._caseSensitiveBtn.setAttribute("aria-pressed", "false"), this._caseSensitiveBtn.title = this.editor.label("Match case"), this._caseSensitiveBtn.addEventListener("click", () => {
|
|
3586
3589
|
this._caseSensitive = !this._caseSensitive, this._caseSensitiveBtn.setAttribute("aria-pressed", String(this._caseSensitive)), this._caseSensitiveBtn.classList.toggle("fj:me-btn-active", this._caseSensitive), this._search();
|
|
3587
3590
|
});
|
|
3588
3591
|
let i = document.createElement("button");
|
|
3589
|
-
i.type = "button", i.className = "fj:me-btn fj:me-btn-xs fj:me-btn-ghost fj:text-xs fj:px-1.5", i.textContent = "Replace", i.setAttribute("aria-label", "Toggle replace row"), i.addEventListener("click", () => {
|
|
3592
|
+
i.type = "button", i.className = "fj:me-btn fj:me-btn-xs fj:me-btn-ghost fj:text-xs fj:px-1.5", i.textContent = this.editor.label("Replace"), i.setAttribute("aria-label", this.editor.label("Toggle replace row")), i.addEventListener("click", () => {
|
|
3590
3593
|
this._replaceRow.hidden = !this._replaceRow.hidden, this._replaceRow.hidden || this._replaceInput.focus();
|
|
3591
3594
|
});
|
|
3592
3595
|
let a = this._iconBtn("Close", "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M18 6 6 18\"/><path d=\"m6 6 12 12\"/></svg>");
|
|
3593
|
-
a.addEventListener("click", () => this.close()), t.append(this._findInput, this._countEl, n, r, this._caseSensitiveBtn, i, a), this._replaceRow = document.createElement("div"), this._replaceRow.className = "fj:flex fj:items-center fj:gap-1", this._replaceRow.hidden = !0, this._replaceInput = document.createElement("input"), this._replaceInput.type = "text", this._replaceInput.placeholder = "Replace", this._replaceInput.className = "fj:me-input fj:me-input-xs fj:w-36", this._replaceInput.setAttribute("aria-label", "Replace with"), this._replaceInput.addEventListener("keydown", (e) => {
|
|
3596
|
+
a.addEventListener("click", () => this.close()), t.append(this._findInput, this._countEl, n, r, this._caseSensitiveBtn, i, a), this._replaceRow = document.createElement("div"), this._replaceRow.className = "fj:flex fj:items-center fj:gap-1", this._replaceRow.hidden = !0, this._replaceInput = document.createElement("input"), this._replaceInput.type = "text", this._replaceInput.placeholder = this.editor.label("Replace"), this._replaceInput.className = "fj:me-input fj:me-input-xs fj:w-36", this._replaceInput.setAttribute("aria-label", this.editor.label("Replace with")), this._replaceInput.addEventListener("keydown", (e) => {
|
|
3594
3597
|
e.key === "Enter" && this._replace(), e.key === "Escape" && (e.preventDefault(), this.close());
|
|
3595
3598
|
});
|
|
3596
3599
|
let o = document.createElement("button");
|
|
3597
|
-
o.type = "button", o.className = "fj:me-btn fj:me-btn-xs fj:me-btn-ghost fj:text-xs fj:px-1.5", o.textContent = "Replace", o.addEventListener("click", () => this._replace());
|
|
3600
|
+
o.type = "button", o.className = "fj:me-btn fj:me-btn-xs fj:me-btn-ghost fj:text-xs fj:px-1.5", o.textContent = this.editor.label("Replace"), o.addEventListener("click", () => this._replace());
|
|
3598
3601
|
let s = document.createElement("button");
|
|
3599
|
-
s.type = "button", s.className = "fj:me-btn fj:me-btn-xs fj:me-btn-ghost fj:text-xs fj:px-1.5", s.textContent = "All", s.setAttribute("aria-label", "Replace all"), s.addEventListener("click", () => this._replaceAll()), this._replaceRow.append(this._replaceInput, o, s), e.append(t, this._replaceRow), this.editor.markdownEditorDiv.appendChild(e), this._panel = e;
|
|
3602
|
+
s.type = "button", s.className = "fj:me-btn fj:me-btn-xs fj:me-btn-ghost fj:text-xs fj:px-1.5", s.textContent = this.editor.label("All"), s.setAttribute("aria-label", this.editor.label("Replace all")), s.addEventListener("click", () => this._replaceAll()), this._replaceRow.append(this._replaceInput, o, s), e.append(t, this._replaceRow), this.editor.markdownEditorDiv.appendChild(e), this._panel = e;
|
|
3600
3603
|
}
|
|
3601
3604
|
_iconBtn(e, t) {
|
|
3602
3605
|
let n = document.createElement("button");
|
|
3603
|
-
n.type = "button", n.className = "fj:me-btn fj:me-btn-xs fj:me-btn-square fj:me-btn-ghost", n.setAttribute("aria-label", e), n.innerHTML = t;
|
|
3606
|
+
n.type = "button", n.className = "fj:me-btn fj:me-btn-xs fj:me-btn-square fj:me-btn-ghost", n.setAttribute("aria-label", this.editor.label(e)), n.innerHTML = t;
|
|
3604
3607
|
let r = n.querySelector("svg");
|
|
3605
3608
|
return r && r.setAttribute("aria-hidden", "true"), n;
|
|
3606
3609
|
}
|
|
@@ -3760,13 +3763,17 @@ var Dr = class {
|
|
|
3760
3763
|
}
|
|
3761
3764
|
}, jr = new rt({ breaks: !0 }), Mr = class e {
|
|
3762
3765
|
constructor(e, t = {}) {
|
|
3763
|
-
this.usertextarea = typeof e == "string" ? document.querySelector(e) : e, this.options = t, this.mode = t.mode || "plain", this.preview = !this.options.toolbar || this.options.toolbar.includes("preview"), this.footerOptions = this._parseFooterOptions(t.footer), this.variables = this._parseVariables(t.toolbar), this.imageUpload = this._parseImageUpload(t.toolbar), this.previewTimer = null, this.init(), this.undoRedoManager = new Sr(this), this.listManager = new wr(this), this.indentManager = new Cr(this), this.shortcutManager = new Dr(this), this.findReplace = new Or(this), this.pasteManager = new Ar(this), this._warnIfStylesheetMissing();
|
|
3766
|
+
this.usertextarea = typeof e == "string" ? document.querySelector(e) : e, this.options = t, this.mode = t.mode || "plain", this.preview = !this.options.toolbar || this.options.toolbar.includes("preview"), this.footerOptions = this._parseFooterOptions(t.footer), this.variables = this._parseVariables(t.toolbar), this.imageUpload = this._parseImageUpload(t.toolbar), this.previewTimer = null, this.init(), this.undoRedoManager = new Sr(this), this.listManager = new wr(this), this.indentManager = new Cr(this), this.shortcutManager = new Dr(this), this.findReplace = new Or(this), this.pasteManager = new Ar(this), this._warnIfStylesheetMissing(), this._warnIfLocaleIsACode();
|
|
3767
|
+
}
|
|
3768
|
+
_warnIfLocaleIsACode() {
|
|
3769
|
+
let e = this.options.locale;
|
|
3770
|
+
typeof e == "string" && console.error(`[MarkdownEditor] locale: '${e}' has no effect. It takes the strings, not a code:\n import ${/^[a-z]{2}$/i.test(e) ? e : "strings"} from 'markdown-text-editor/locale/${e}.js'\n new MarkdownEditor(el, { locale: ${/^[a-z]{2}$/i.test(e) ? e : "strings"} })`);
|
|
3764
3771
|
}
|
|
3765
3772
|
_warnIfStylesheetMissing() {
|
|
3766
3773
|
e._cssWarned || this.editorContainer?.isConnected && (getComputedStyle(this.editorContainer).getPropertyValue("--mte-css").trim() || (e._cssWarned = !0, console.error("[MarkdownEditor] Stylesheet not loaded, so the editor will look unstyled.\n Bundlers: import 'markdown-text-editor/style.css'\n Browsers: <link rel=\"stylesheet\" href=\".../dist/markdown-text-editor.css\">")));
|
|
3767
3774
|
}
|
|
3768
3775
|
label(e) {
|
|
3769
|
-
return this.options.labels?.[e] ?? e;
|
|
3776
|
+
return (this.options.locale ?? this.options.labels)?.[e] ?? e;
|
|
3770
3777
|
}
|
|
3771
3778
|
_parseImageUpload(e) {
|
|
3772
3779
|
let t = (Array.isArray(e) ? e : []).find((e) => e && typeof e == "object" && e.image)?.image?.fileInput;
|
|
@@ -3867,7 +3874,7 @@ var Dr = class {
|
|
|
3867
3874
|
addFooter() {
|
|
3868
3875
|
if (!this.footerOptions) return;
|
|
3869
3876
|
let { line: e, col: t, chars: n, words: r } = this.footerOptions;
|
|
3870
|
-
(e || t || n || r) && (this.footer = new er(this.editorContainer, this.footerOptions), this.footer.update(this.usertextarea), this._footerUpdate = () => this.footer.update(this.usertextarea), this.usertextarea.addEventListener("input", this._footerUpdate), this.usertextarea.addEventListener("click", this._footerUpdate), this.usertextarea.addEventListener("keyup", this._footerUpdate), this.usertextarea.addEventListener("focus", this._footerUpdate));
|
|
3877
|
+
(e || t || n || r) && (this.footer = new er(this.editorContainer, this.footerOptions, (e) => this.label(e)), this.footer.update(this.usertextarea), this._footerUpdate = () => this.footer.update(this.usertextarea), this.usertextarea.addEventListener("input", this._footerUpdate), this.usertextarea.addEventListener("click", this._footerUpdate), this.usertextarea.addEventListener("keyup", this._footerUpdate), this.usertextarea.addEventListener("focus", this._footerUpdate));
|
|
3871
3878
|
}
|
|
3872
3879
|
addToolbar() {
|
|
3873
3880
|
this.toolbarInstance = new Qn(this, this.options.toolbar || [
|